package server

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"cs0512/fmr/internal/pkg/middleware"
	"cs0512/fmr/internal/pkg/response"

	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/marmotedu/log"
	"golang.org/x/sync/errgroup"
)

// 通用API服务
type GenericAPIServer struct {
	// gin框架
	*gin.Engine

	//中间件名称
	middlewares []string

	//模式
	mode string

	// Http服务
	insecureServer *http.Server

	// HTTP server 配置信息
	InsecureServingInfo *InsecureServingInfo

	// 服务关闭时强制退出时间
	ShutdownTimeout time.Duration

	// HTTP TLS服务
	secureServer *http.Server

	// configuration TLS server 配置信息
	SecureServingInfo *SecureServingInfo

	// 健康检查
	healthz bool
}

// New一个服务实例
func NewServer(c *CompletedConfig) *GenericAPIServer {
	s := &GenericAPIServer{
		SecureServingInfo:   c.SecureServing,
		InsecureServingInfo: c.InsecureServing,
		mode:                c.Mode,
		middlewares:         c.Middlewares,
		Engine:              gin.New(),
		healthz:             c.Healthz,
	}

	initGenericAPIServer(s)

	return s
}

// 初始化通用API服务，安装监控组件，安装中间件，安装Handler
func initGenericAPIServer(s *GenericAPIServer) {
	// do some setup
	s.Setup()
	s.InstallMiddlewares()
	s.InstallAPIs()
}

// 一些gin的初始化工作
func (s *GenericAPIServer) Setup() {
	gin.SetMode(s.mode)
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		log.Infof("%-6s %-s --> %s (%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
	}
}

// 加载中间件
func (s *GenericAPIServer) InstallMiddlewares() {
	// 安装必要的中间件
	//RequestID中间件
	//s.Use(middleware.RequestID())
	//Context 中间件
	//s.Use(middleware.Context())
	// 限流中间件
	// s.Use(limits.RequestSizeLimiter(10))

	// 安装服务配置中指定的中间件
	for _, m := range s.middlewares {
		mw, ok := middleware.Middlewares[m]
		if !ok {
			log.Warnf("can not find middleware: %s", m)

			continue
		}

		log.Infof("install middleware: %s", m)
		s.Use(mw)
	}
}

// 安装通用API handler,如健康检查，Metrics Prometheus，版本号，pprof
func (s *GenericAPIServer) InstallAPIs() {
	// install healthz handler
	if s.healthz {
		s.GET("/healthz", func(c *gin.Context) {
			response.OK(c, map[string]string{"status": "ok"}, "")
		})
	}

	// install pprof handler
	// 后续增加配置开关，生产环境需要关闭TODO...
	pprof.Register(s.Engine)

	// 查看版本
	s.GET("/version", func(c *gin.Context) {
		response.OK(c, map[string]string{"Version": "0.0.1"}, "")
	})
}

// 启动运行Http服务. 如在初始化时，端口无法监听则返回
func (s *GenericAPIServer) Run() error {
	//  为了实现可伸缩性，请在此处使用自定义HTTP配置模式
	s.insecureServer = &http.Server{
		Addr:    s.InsecureServingInfo.Address,
		Handler: s,
		// ReadTimeout:    10 * time.Second,
		// WriteTimeout:   10 * time.Second,
		// MaxHeaderBytes: 1 << 20,

	}

	//  为了实现可伸缩性，请在此处使用自定义HTTP配置模式
	s.secureServer = &http.Server{
		Addr:    s.SecureServingInfo.Address(),
		Handler: s,
		// ReadTimeout:    10 * time.Second,
		// WriteTimeout:   10 * time.Second,
		// MaxHeaderBytes: 1 << 20,
	}

	var eg errgroup.Group

	// Initializing the server in a goroutine so that
	// it won't block the graceful shutdown handling below
	eg.Go(func() error {
		log.Infof("Start to listening the incoming requests on http address: %s", s.InsecureServingInfo.Address)

		if err := s.insecureServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err.Error())

			return err
		}

		log.Infof("Server on %s stopped", s.InsecureServingInfo.Address)

		return nil
	})

	eg.Go(func() error {
		//没有配置证书或监听端口，则不启动TLS服务
		key, cert := s.SecureServingInfo.CertKey.KeyFile, s.SecureServingInfo.CertKey.CertFile
		if cert == "" || key == "" || s.SecureServingInfo.BindPort == 0 {
			return nil
		}

		log.Infof("Start to listening the incoming requests on https address: %s", s.SecureServingInfo.Address())

		if err := s.secureServer.ListenAndServeTLS(cert, key); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err.Error())

			return err
		}

		log.Infof("Server on %s stopped", s.SecureServingInfo.Address())

		return nil
	})

	// Ping the server to make sure the router is working.
	// Ping 自己创建的服务，做健康检查
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if s.healthz {
		if err := s.ping(ctx); err != nil {
			return err
		}
	}

	if err := eg.Wait(); err != nil {
		log.Fatal(err.Error())
	}

	return nil
}

// Close graceful shutdown the api server.
// 服务关闭
func (s *GenericAPIServer) Close() {
	// The context is used to inform the server it has 10 seconds to finish
	// the request it is currently handling
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := s.secureServer.Shutdown(ctx); err != nil {
		log.Warnf("Shutdown secure server failed: %s", err.Error())
	}

	if err := s.insecureServer.Shutdown(ctx); err != nil {
		log.Warnf("Shutdown insecure server failed: %s", err.Error())
	}
}

// ping pings the http server to make sure the router is working.
func (s *GenericAPIServer) ping(ctx context.Context) error {
	url := fmt.Sprintf("http://%s/healthz", s.InsecureServingInfo.Address)
	if strings.Contains(s.InsecureServingInfo.Address, "0.0.0.0") {
		url = fmt.Sprintf("http://127.0.0.1:%s/healthz", strings.Split(s.InsecureServingInfo.Address, ":")[1])
	}

	for {
		// Change NewRequest to NewRequestWithContext and pass context it
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
		if err != nil {
			return err
		}
		// Ping the server by sending a GET request to `/healthz`.

		resp, err := http.DefaultClient.Do(req)
		if err == nil && resp.StatusCode == http.StatusOK {
			log.Info("The router has been deployed successfully.")

			resp.Body.Close()

			return nil
		}

		// Sleep for a second to continue the next ping.
		log.Info("Waiting for the router, retry in 1 second.")
		time.Sleep(1 * time.Second)

		select {
		case <-ctx.Done():
			log.Fatal("can not ping http server within the specified time interval.")
		default:
		}
	}
	// return fmt.Errorf("the router has no response, or it might took too long to start up")
}
